/*
 * © 2024 huayunliufeng保留所有权利, 依据MIT许可证发布。
 * 请勿更改或删除版权声明或此文件头。
 * 此代码是免费软件, 您可以重新分发和/或修改它。
 * 开源是希望它有用, 但不对代码做任何保证。
 * 如有疑问请联系: huayunliufeng@163.com
 */

package io.github.huayunliufeng.common.utils;

import io.github.huayunliufeng.common.intface.*;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * 当某个方法执行抛异常, 但是影响不大时, 可以尝试这些方法, 他们将待执行的方法使用try-catch包装起来, 如果抛了异常, 将打印出warn级别的异常信息。
 *
 * @author huayunliufeng
 * @date_time 2024/3/23 22:48
 */
@Slf4j
public class HylfFunUtil {

    /**
     * 执行无参无返回值的方法, 有异常将打印日志并根据status判断是否终止程序
     *
     * @param function  function
     * @param status    非0值将在执行失败时终止程序
     * @param logFormat 日志格式
     * @param args      日志参数
     */
    public static <T> void methodVoidExec(final VoidFunction function, int status, final String logFormat, final Object... args) {
        try {
            function.accept();
        } catch (Exception e) {
            log.warn("方法{}执行失败。", function, e);
            if (logFormat != null) {
                log.error(logFormat, args);
            }
        }
        if (status != 0) {
            System.exit(status);
        }
    }

    /**
     * 执行无参无返回值的方法, 有异常将打印日志但不终止程序
     *
     * @param function  function
     * @param logFormat 日志格式
     * @param args      日志参数
     */
    public static <T> void methodVoidExec(final VoidFunction function, final String logFormat, final Object... args) {
        methodVoidExec(function, 0, logFormat, args);
    }

    /**
     * 执行无参无返回值的方法, 有异常将打印日志, 但不中断
     *
     * @param function function
     */
    public static <T> void methodVoidExec(final VoidFunction function) {
        methodVoidExec(function, null);
    }

    /**
     * 执行无参无返回值的方法, 有异常将打印日志并根据status判断是否终止程序
     *
     * @param status   非0值将在执行失败时终止程序
     * @param function function
     */
    public static <T> void methodVoidExec(final VoidFunction function, int status) {
        methodVoidExec(function, status, null);
    }


    /**
     * 执行无返回值一个入参的方法, 有异常将打印日志并根据status判断是否终止程序
     *
     * @param function  function
     * @param t         方法入参
     * @param status    非0值将在执行失败时终止程序
     * @param logFormat 日志格式
     * @param args      日志参数
     */
    public static <T> void methodOneVoidExec(final OneVoidFunction<T> function, final T t, int status, final String logFormat, final Object... args) {
        try {
            function.accept(t);
        } catch (Exception e) {
            log.warn("方法{}执行失败。[入参 = {}]", function, t, e);
            if (logFormat != null) {
                log.error(logFormat, args);
            }
        }
        if (status != 0) {
            System.exit(status);
        }
    }

    /**
     * 执行无返回值一个入参的方法, 有异常将打印日志但不终止程序
     *
     * @param function  function
     * @param t         方法入参
     * @param logFormat 日志格式
     * @param args      日志参数
     */
    public static <T> void methodOneVoidExec(final OneVoidFunction<T> function, final T t, final String logFormat, final Object... args) {
        methodOneVoidExec(function, t, 0, logFormat, args);
    }

    /**
     * 执行无返回值一个入参的方法, 有异常将打印日志, 但不终止程序
     *
     * @param function function
     * @param t        方法入参
     */
    public static <T> void methodOneVoidExec(final OneVoidFunction<T> function, final T t) {
        methodOneVoidExec(function, t, 0, null);
    }

    /**
     * 执行无返回值一个入参的方法, 有异常将打印日志并根据status判断是否终止程序
     *
     * @param function function
     * @param t        方法入参
     * @param status   非0值将在执行失败时终止程序
     */
    public static <T> void methodOneVoidExec(final OneVoidFunction<T> function, final T t, int status) {
        methodOneVoidExec(function, t, status, null);
    }

    /**
     * 执行无返回值两个入参的方法, 有异常将打印日志并根据status判断是否终止程序
     *
     * @param function  function
     * @param t         入参1
     * @param u         入参2
     * @param status    非0值将在执行失败时终止程序
     * @param logFormat 日志格式
     * @param args      日志参数
     */
    public static <T, U> void methodTwoVoidExec(final TwoVoidFunction<T, U> function, final T t, final U u, int status, final String logFormat, final Object... args) {
        try {
            function.accept(t, u);
        } catch (Exception e) {
            log.warn("方法{}执行失败。[入参 = {}, {}]", function, t, u, e);
            if (logFormat != null) {
                log.error(logFormat, args);
            }
        }
        if (status != 0) {
            System.exit(status);
        }
    }

    /**
     * 执行无返回值两个入参的方法, 有异常将打印日志但不终止程序
     *
     * @param function  function
     * @param t         入参1
     * @param u         入参2
     * @param logFormat 日志格式
     * @param args      日志参数
     */
    public static <T, U> void methodTwoVoidExec(final TwoVoidFunction<T, U> function, final T t, final U u, final String logFormat, final Object... args) {
        methodTwoVoidExec(function, t, u, 0, logFormat, args);
    }

    /**
     * 执行无返回值两个入参的方法, 有异常将打印日志, 但不终止程序
     *
     * @param function function
     * @param t        入参1
     * @param u        入参2
     */
    public static <T, U> void methodTwoVoidExec(final TwoVoidFunction<T, U> function, final T t, final U u) {
        methodTwoVoidExec(function, t, u, null);
    }

    /**
     * 执行无返回值两个入参的方法, 有异常将打印日志并根据status判断是否终止程序
     *
     * @param function function
     * @param t        入参1
     * @param u        入参2
     * @param status   非0值将在执行失败时终止程序
     */
    public static <T, U> void methodTwoVoidExec(final TwoVoidFunction<T, U> function, final T t, final U u, int status) {
        methodTwoVoidExec(function, t, u, status, null);
    }

    /**
     * 执行有返回值无入参的方法, 有异常将打印日志并根据status判断是否终止程序或返回null
     *
     * @param function  function
     * @param logFormat 日志格式
     * @param status    非0值将在执行失败时终止程序
     * @param args      日志参数
     * @return R
     */
    public static <R> R methodVoidReturnExec(final VoidReturnFunction<R> function, int status, final String logFormat, final Object... args) {
        try {
            return function.apply();
        } catch (Exception e) {
            log.warn("方法{}执行失败。", function, e);
            if (logFormat != null) {
                log.error(logFormat, args, e);
            }
        }
        if (status != 0) {
            System.exit(status);
        }
        return null;
    }

    /**
     * 执行有返回值无入参的方法, 有异常将打印日志并返回null
     *
     * @param function  function
     * @param logFormat 日志格式
     * @param args      日志参数
     * @return R
     */
    public static <R> R methodVoidReturnExec(final VoidReturnFunction<R> function, final String logFormat, final Object... args) {
        return methodVoidReturnExec(function, 0, logFormat, args);
    }

    /**
     * 执行有返回值无入参的方法, 有异常将打印日志并返回null
     *
     * @param function function
     * @param <R>      返回值类型
     * @return R
     */
    public static <R> R methodVoidReturnExec(final VoidReturnFunction<R> function) {
        return methodVoidReturnExec(function, null);
    }

    /**
     * 执行有返回值无入参的方法, 有异常将打印日志并根据status判断是否终止程序或返回null
     *
     * @param function function
     * @param status   非0值将在执行失败时终止程序
     * @param <R>      返回值类型
     * @return R
     */
    public static <R> R methodVoidReturnExec(final VoidReturnFunction<R> function, int status) {
        return methodVoidReturnExec(function, status, null);
    }

    /**
     * 执行有返回值一个入参的方法, 有异常将打印日志并根据status判断是否终止程序或返回null
     *
     * @param function  function
     * @param t         方法入参
     * @param status    非0值将在执行失败时终止程序
     * @param logFormat 日志格式
     * @param args      日志参数
     * @return R
     */
    public static <T, R> R methodOneReturnExec(final OneReturnFunction<T, R> function, final T t, int status, final String logFormat, final Object... args) {
        try {
            return function.apply(t);
        } catch (Exception e) {
            log.warn("方法{}执行失败。[入参 = {}]", function, t, e);
            if (logFormat != null) {
                log.error(logFormat, args);
            }
        }
        if (status != 0) {
            System.exit(status);
        }
        return null;
    }

    /**
     * 执行有返回值一个入参的方法, 有异常将打印日志并返回null
     *
     * @param function  function
     * @param t         方法入参
     * @param logFormat 日志格式
     * @param args      日志参数
     * @return R
     */
    public static <T, R> R methodOneReturnExec(final OneReturnFunction<T, R> function, final T t, final String logFormat, final Object... args) {
        return methodOneReturnExec(function, t, 0, logFormat, args);
    }

    /**
     * 执行有返回值一个入参的方法, 有异常将打印日志并返回null
     *
     * @param function function
     * @param t        方法入参
     * @return R
     */
    public static <T, R> R methodOneReturnExec(final OneReturnFunction<T, R> function, final T t) {
        return methodOneReturnExec(function, t, null);
    }

    /**
     * 执行有返回值一个入参的方法, 有异常将打印日志并根据status判断是否终止程序或返回null
     *
     * @param function function
     * @param t        方法入参
     * @param status   非0值将在执行失败时终止程序
     * @return R
     */
    public static <T, R> R methodOneReturnExec(final OneReturnFunction<T, R> function, final T t, int status) {
        return methodOneReturnExec(function, t, status, null);
    }

    /**
     * 执行有返回值两个入参的方法, 有异常将打印日志并根据status判断是否终止程序或返回null
     *
     * @param function  function
     * @param t         入参1
     * @param u         入参2
     * @param status    非0值将在执行失败时终止程序
     * @param logFormat 日志格式
     * @param args      日志参数
     * @return R
     */
    public static <T, U, R> R methodTwoReturnExec(final TwoReturnFunction<T, U, R> function, final T t, final U u, int status, final String logFormat, final Object... args) {
        try {
            return function.apply(t, u);
        } catch (Exception e) {
            log.warn("方法{}执行失败。[入参 = {}, {}]", function, t, u, e);
            if (logFormat != null) {
                log.error(logFormat, args);
            }
        }
        if (status != 0) {
            System.exit(status);
        }
        return null;
    }

    /**
     * 执行有返回值两个入参的方法, 有异常将打印日志并返回null
     *
     * @param function  function
     * @param t         入参1
     * @param u         入参2
     * @param logFormat 日志格式
     * @param args      日志参数
     * @return R
     */
    public static <T, U, R> R methodTwoReturnExec(final TwoReturnFunction<T, U, R> function, final T t, final U u, final String logFormat, final Object... args) {
        return methodTwoReturnExec(function, t, u, 0, logFormat, args);
    }

    /**
     * 执行有返回值两个入参的方法, 有异常将打印日志并返回null
     *
     * @param function function
     * @param t        入参1
     * @param u        入参2
     * @return R
     */
    public static <T, U, R> R methodTwoReturnExec(final TwoReturnFunction<T, U, R> function, final T t, final U u) {
        return methodTwoReturnExec(function, t, u, null);
    }

    /**
     * 执行有返回值两个入参的方法, 有异常将打印日志并根据status判断是否终止程序或返回null
     *
     * @param function function
     * @param t        入参1
     * @param u        入参2
     * @param status   非0值将在执行失败时终止程序
     * @return R
     */
    public static <T, U, R> R methodTwoReturnExec(final TwoReturnFunction<T, U, R> function, final T t, final U u, int status) {
        return methodTwoReturnExec(function, t, u, status, null);
    }

    /**
     * 如果t是null, 返回新的实例, 否则返回t.
     *
     * @param t        t
     * @param supplier supplier
     * @return t
     */
    public static <T> T getOrDefault(T t, final Supplier<T> supplier) {
        if (t == null) {
            t = supplier.get();
        }
        return t;
    }

    /**
     * 给map添加新的值, 如果map是null, 则初始化map, 然后根据传入的判空条件判断是不是需要添加该值。
     *
     * @param map           map
     * @param supplier      supplier
     * @param key           判断的key
     * @param nullPredicate 判空条件
     * @param value         新值
     * @return map map
     */
    public static <K, V> Map<K, V> mapAddValue(Map<K, V> map, final Supplier<Map<K, V>> supplier, final K key, final Predicate<V> nullPredicate, final Supplier<V> value) {
        try {
            map = getOrDefault(map, supplier);
            V v = map.get(key);
            if (nullPredicate.test(v)) {
                map.put(key, value.get());
            }
        } catch (Exception e) {
            log.warn("方法执行失败。元素未添加成功。[map = {}, supplier = {}, key = {}, value = {}]",
                    map,
                    supplier,
                    key,
                    value,
                    e);
        }
        return map;
    }

    /**
     * <p>给map添加新的值, 如果map是null, 则初始化map, 然后根据传入的判空条件判断是不是需要添加该值。</p>
     * <p>默认提供HashMap, 使用Objects::isNull判空</p>
     *
     * @param map   map
     * @param key   判断的key
     * @param value 新值
     * @return map map
     */
    public static <K, V> Map<K, V> mapAddValue(final Map<K, V> map, final K key, final Supplier<V> value) {
        return mapAddValue(map, HashMap::new, key, Objects::isNull, value);
    }

    /**
     * 自动关闭资源.
     *
     * @param closeable closeable
     */
    public static void autoClose(final AutoCloseable closeable) {
        HylfFunUtil.methodVoidExec(() -> {
            if (closeable != null) {
                closeable.close();
            }
        });
    }
}
